#pragma once
#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <vector>
#include <string>
#include <unordered_map>
#include <pthread.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"
#include "RingQueue.hpp"
#include "lockGuard.hpp"
#include "Thread.hpp"

using namespace std;
// 服务器
namespace udp_server
{
    const static uint16_t DefaultPort = 8080;
    using func_t = function<string(string)>; // 定义回调函数的类型：返回值是string，参数是string
    class UdpServer
    {
    public:
        // 构造
        UdpServer(uint16_t port = DefaultPort): port_(port)
        {
            cout << "server addr:" << port_ << endl;
            pthread_mutex_init(&lock,nullptr);
            p = new Thread(1,std::bind(&UdpServer::Recv,this));     //需要传两个参数，使用bind
            c = new Thread(2,std::bind(&UdpServer::Broadacst,this));
            cout<<"生产者和消费者线程对象创建成功！"<<endl;
        }
        /*初始化*/
        void start()
        {
            // 1.创建socket套接字--打开网络文件
            sock_ = socket(AF_INET, SOCK_DGRAM, 0); // AF_INET==PF_INET:ipv4地址族 SOCK_DGRAM:数据报套接字,用于UDP
            if (sock_ == -1)                        /*创建失败*/
            {
                cerr << "socket create error：" << strerror(errno) << endl;
                exit(SOCKET_ERROR); // 退出->退出码：SOCKET_ERROR
            }
            cout << "socket create success：" << sock_ << endl;
            // 2.给服务器绑定IP地址和端口号
            /*int bind(int socket, const struct sockaddr *address,socklen_t address_len);*/
            struct sockaddr_in local;     // local局部变量，用户空间的特定函数的栈帧上，不在内核中
            bzero(&local, sizeof(local)); // 初始化为0
            local.sin_family = AF_INET;   // 网络通信
            /*端口号
            客户端和服务器在进行网络通信时，需要知道相互之间ip和端口号，也就是说在网络通信传输数据中存在端口号这个数据
            所以需要将数据由主机序列变成网络序列
            端口号-->typedef uint16_t in_port_t
            用htons函数将端口号进行大小端的转化，
            */
            local.sin_port = htons(port_);
            /*IP地址
              1)将字符串风格的IP地址，转化成为4字节int，"1.1.1.1"->uint32_t->能不能强制类型转换？不能，这里需要转化
              2)需要将数据由主机序列变成网络序列
              直接使用inet_addr函数完成以上两种功能
              3)云服务器不需要自己绑定IP，只需要传端口号port
            */
            // local.sin_addr.s_addr = inet_addr(ip_.c_str());            //IP地址
            local.sin_addr.s_addr = INADDR_ANY; // 让我们udpserver在启动的时候，bind本主机上的任意IP
            if (bind(sock_, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                cerr << "bind error：" << strerror(errno) << endl;
                exit(BIND_ERROR);
            }
            // cout<<"bind success："<<local.sin_addr.s_addr<<local.sin_port<<endl;
            cout << "bind success：" << sock_ << endl;

            p->run();
            c->run();
        }
        /*启动*/
        // void Start()
        // {
        //     char buff[1024]; // 存储客户端发来的数据的缓冲区
        //     //cout << "Start success" << endl;
        //     struct sockaddr_in peer;  //存储客户端主机的地址
        //     socklen_t len = sizeof(peer);
        //     memset(&peer, 0, sizeof(peer)); // peer里面数据乱码-->初始化
        //     while (true)                    // 一直运行
        //     {
        //         /*ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);*/
        //         // 1.接收客户端的数据--flags取0阻塞式接收
        //         //cout << "before server receive massage success " << endl;
        //         ssize_t n = recvfrom(sock_, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&peer, &len);
        //         if (n > 0)
        //         {
        //             buff[n] = 0; // 数据传输是字节流，要变成字符串，在末尾加上'/0'即0
        //         }
        //         else
        //             continue;
        //         //cout << "server receive massage success " << endl;
        //         // 打印数据
        //         // 获取客户端的IP和端口号port
        //         string clientip = inet_ntoa(peer.sin_addr); // 将客户端的IP由网络序列变成主机序列
        //         uint16_t clientport = ntohs(peer.sin_port); // 将客户端的端口号port由网络序列变成主机序列
        //         cout << clientip << "-" << clientport << "#" << buff << endl;

        //         //对客户端的数据进行业务处理，然后再传回去
        //         string buffer = func_(buff);

        //         // 2.将客户端发来的数据转发回去
        //         sendto(sock_, buffer.c_str(), buffer.size(), 0, (struct sockaddr *)&peer, len);
        //         //cout << "server send to massage success " << endl;
        //     }
        // }
        void AddUser(const string &name, struct sockaddr_in &peer) // 未存在的用户添加
        {
            LockGuard lockguard(&lock); //加锁保护
            if (onlineuser.find(name) == onlineuser.end())
            {
                onlineuser.insert(pair<string, struct sockaddr_in>(name, peer));
            }
        }
        void Recv()
        {
            char buff[1024];         // 存储客户端发来的数据的缓冲区
            struct sockaddr_in peer; // 存储客户端主机的地址
            socklen_t len = sizeof(peer);
            memset(&peer, 0, sizeof(peer)); // peer里面数据乱码-->初始化
            while (true)                    // 一直运行
            {
                /*ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);*/
                // 1.接收客户端的数据--flags取0阻塞式接收
                ssize_t n = recvfrom(sock_, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&peer, &len);
                if (n > 0)
                {
                    buff[n] = 0; // 数据传输是字节流，要变成字符串，在末尾加上'/0'即0
                }
                else
                    continue;
                // 获取客户端的IP和端口号port
                string clientip = inet_ntoa(peer.sin_addr); // 将客户端的IP由网络序列变成主机序列
                uint16_t clientport = ntohs(peer.sin_port); // 将客户端的端口号port由网络序列变成主机序列
                cout << clientip << "-" << clientport << "#" << buff << endl;
                string name;
                name = clientip + '-' + to_string(clientport);
                AddUser(name, peer); // 添加用户
                // 接收数据：哪台主机+消息
                string massage = name + ">>" + buff;
                rq.push(massage);
            }
        }
        void Broadacst()
        {
            while (true)
            {
                // 获取消息
                string sendmassage;
                rq.pop(&sendmassage);
                vector<struct sockaddr_in> v;
                {
                    LockGuard lockguard(&lock);  //加锁保护
                    for (auto e : onlineuser)
                    {
                        v.push_back(e.second);
                    }
                }
                for (auto user : v)
                {
                    sendto(sock_, sendmassage.c_str(), sendmassage.size(), 0, (struct sockaddr *)&user, sizeof(user));
                    cout << "send done...." << sendmassage << endl;
                }
            }
        }

        // 析构
        ~UdpServer()
        {
            pthread_mutex_destroy(&lock);
            p->join();
            c->join();

            delete p;
            delete c;
        }

    private:
        int sock_;                                     // 套接字接口文件标识符
        uint16_t port_;                                // 端口号
        // func_t func_;                                  // 接收和发送数据只是网络的IO问题，我们服务器主要是要进行业务处理：回调函数--处理业务
        unordered_map<string, sockaddr_in> onlineuser; // 名字+地址
        RingQueue<string> rq;                          // 接收消息
        pthread_mutex_t lock;                          // 加一把锁保护onlineuser空间
        Thread* p;                                     //生产者    
        Thread* c;                                     //消费者
    };
}